home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Snippets / Networking / ENET sample / ENETTest.p < prev    next >
Encoding:
Text File  |  1993-06-30  |  16.0 KB  |  465 lines  |  [TEXT/PJMM]

  1.  
  2. { The following program sample demonstrates how one can use the Ethernet routines }
  3. { described in Inside Macintosh volume VI.  To simplify this sample, the program}
  4. { does not include the traditional Macintosh Toolbox initialization code.  The program }
  5. { instead demonstrates the basic use of the various Control calls to the ENET driver. }
  6. {}
  7. {    To run this program, you'll need two Mac connected to Ethernet.  Have Macsbug installed}
  8. {  on both systems, as it is the user interface to demonstrate that the program has entered}
  9. {  the completion routine when the ERead call completes.  Launch a copy of this }
  10. {  on the first system, then launch a second copy on the other system.  When the second }
  11. {  system sends the packet to the first, the completion routine gets called and you drop into}
  12. {  Macsbug momentarily.  Assuming that things go well, that's the only time you'll enter}
  13. {  macsbug.  To get out of the program, click the mouse button.  }
  14. {}
  15. {    Written By: Rich Kubota }
  16. {    Date 6/93 }
  17. {}
  18.  
  19.  
  20. PROGRAM ENETTest;
  21.  
  22. {$IFC THINK_Pascal}
  23.     USES
  24.         ROMDefs, Slots, AppleTalk, ENET;
  25. {$ELSEC}
  26.     USES
  27.         Types, QuickDraw, Errors, Memory, Devices, ROMDefs, OSUtils, Slots, AppleTalk, ENET;
  28. {$ENDC}
  29.  
  30.     CONST
  31.         kMyProtocol = $9090;     { must be > $5DC or you'll replace the }
  32.                                         { default handler which would be a bad  thing}
  33.  
  34.     TYPE                                { define new EParamBlock type to append }
  35.         MyEParamBlock = RECORD    { an A5 value to the end of the record }
  36.                 pb: EParamBlock;    { This mod is for the demo completion routine }
  37.                 myA5: LONGINT;
  38.             END;
  39.  
  40.         MyEParamBlkPtr = ^MyEParamBlock;
  41.  
  42.     VAR                                { global param block used to track calls to ERead }
  43.         gMyEPB: MyEParamBlock;
  44.         gMultiCastAddr: PACKED ARRAY[1..6] OF byte;
  45.         gDone: Boolean;
  46.  
  47.     PROCEDURE DoError (err: INTEGER);
  48.     BEGIN
  49.         Debugger;                    { not the best interface, but I wanted this to be a }
  50.                                         { a budget example }
  51.     END;
  52.  
  53.     PROCEDURE ProcessData (aptr: Ptr);
  54.     BEGIN
  55.         DebugStr('Processing data;g');    { again, not the best user interface, }
  56.                                         { but there's not a whole lot you can }
  57.                                         { do at interrupt time }
  58.     END;
  59.  
  60.     PROCEDURE Detach_SamplePH (ENETRefNum: INTEGER);
  61.     FORWARD;
  62.  
  63. {*****************************************************************************}
  64. { The following initializes the global variable gMultiCastAddr so that we     }
  65. { only set it's value here and not have to worry about it's value throughout  }
  66. { the remainder of this program.  In addition we can then use BlockMove to    }
  67. { fill in the various paramblock fields and not have to worry about byte sized}
  68. { values being filled into word sized locations.                                         }
  69. {*****************************************************************************}
  70.  
  71.     PROCEDURE Init_Multicast_Address;
  72.     BEGIN
  73.         gMultiCastAddr[1] := $09;
  74.         gMultiCastAddr[2] := $00;
  75.         gMultiCastAddr[3] := $2B;
  76.         gMultiCastAddr[4] := $00;
  77.         gMultiCastAddr[5] := $00;
  78.         gMultiCastAddr[6] := $04;
  79.     END;
  80.  
  81.  
  82. {*****************************************************************************}
  83. { The following function demonstrates how to identify and to open an Ethernet }
  84. { driver using the Slot Manager and the OpenSlot function.                             }
  85. { This function returns the driver refNum of the opened ENET driver or 0 if   }
  86. { an error occured or an Ethernet card could not be identified                        }
  87. {*****************************************************************************}
  88.  
  89.     FUNCTION Get_And_Open_ENET_Driver: INTEGER;
  90.  
  91.         VAR
  92.             mySBlk: SpBlock;
  93.             myPBRec: ParamBlockRec;
  94.             myErr: OSErr;
  95.             Found: INTEGER;
  96.             ENETRefNum: INTEGER;
  97.             EnetStr: Str15;
  98.             Enet0Str: Str15;
  99.  
  100.     BEGIN
  101.         Found := 0;                { assume no sResource found }
  102.         ENETRefNum := 0;        { indicate no driver found }
  103.  
  104.         WITH mySBlk DO            { set up the spBlock }
  105.             BEGIN
  106.                 spParamData := 1;        { include search of disabled resources. }
  107.                                                 {  Start searching item from spSlot and }
  108.                                                 {  the slots above it as well. }
  109.                 spCategory := catNetwork;
  110.                 spCType := typeEthernet;
  111.                 spDrvrSW := 0;
  112.                 spDrvrHW := 0;
  113.                 spTBMask := 3;            { match only Category and CType fields }
  114.                 spSlot := 0;            { start search from here }
  115.                 spID := 0;                { start search from here }
  116.                 spExtDev := 0;            { ID of the external device }
  117.             END;
  118.  
  119.         {REPEAT}
  120.                             { we could implement a repeat loop to check }
  121.                             { for multiple ENET cards, however in this sample }
  122.                             { we only grab the first one }
  123.         myErr := SGetTypeSRsrc(@mySBlk);
  124.         IF myErr = noErr THEN        { you have found an sResource match }
  125.                                                 { save it for later }
  126.             BEGIN
  127.                 Found := Found + 1;
  128.                     { SaveSInfo(@mySBlk);    { save slot info for later use }
  129.             END;
  130.         {until myErr = smNoMoresRsrcs;}
  131.  
  132.         IF Found > 1 THEN
  133.             BEGIN
  134.                 { If you found more than one sResource, put up a dialog box }
  135.                 {  and let the user choose one.  If any of the sResources you }
  136.                 {  found were disabled, let the user know they are not available. }
  137.                 { For this example we assume that the selected slot is returned }
  138.                 {  in mySBlk.spSlot along with the corresponding sResource ID }
  139.                 { in mySBlk.spID - and that Found remains > 1 to indicate to }
  140.                 {  go ahead and open the driver. }
  141.             END;
  142.  
  143.         IF found <> 0 THEN
  144.             BEGIN
  145.                 EnetStr := '.ENET';
  146.                 WITH myPBRec DO
  147.                     BEGIN
  148.                         ioCompletion := NIL;            { call made synchronously }
  149.                         ioNamePtr := @EnetStr;
  150.                         ioPermssn := fsCurPerm;
  151.                         ioFlags := 0;                    { reserved for driver use }
  152.                         ioSlot := mySBlk.spSlot;    { slot of ENET card to open }
  153.                         ioID := mySBlk.spID            { sResource ID for slot }
  154.                     END;
  155.                 myErr := OpenSlot(@myPBRec, FALSE);    { go open slot }
  156.                 IF myErr = noErr THEN
  157.                     ENETRefNum := myPBRec.ioRefNum;
  158.             END
  159.         ELSE
  160.             BEGIN
  161.                 Enet0Str := '.ENET0';
  162.                 myErr := OpenDriver(Enet0Str, ENETRefNum);
  163.             END;
  164.         IF myErr <> noErr THEN
  165.             DoError(myErr);                            { go handle the error }
  166.  
  167.         Get_And_Open_ENET_Driver := ENETRefNum; { return the refNum or 0 if }
  168.                                                             { unsuccessful }
  169.     END;
  170.  
  171. {*****************************************************************************}
  172. { The following function demonstrates how to add a multicast address          }
  173. { The function returns an result of type OSErr                                }
  174. {*****************************************************************************}
  175.     FUNCTION Add_Multicast_Address (ENETRefNum: Integer): OSErr;
  176.         VAR
  177.             myPB: EParamBlock;
  178.             myErr: OSErr;
  179.  
  180.     BEGIN
  181.             { fill in the multicast address field using BlockMove from the }
  182.             { global multicast variable - C programmers want to ensure that the }
  183.             { eMultiAddr field is correctly defined in the ENET.h file }
  184.             { This field immediately follows the csCode field in the EParamBlock }
  185.             { structure }
  186.         BlockMove(@gMultiCastAddr, @myPB.eMultiAddr, sizeof(gMultiCastAddr));
  187.         myPB.ioRefNum := ENETRefNum;
  188.         myErr := EAddMulti(@myPB, FALSE);
  189.         IF myErr <> noErr THEN
  190.             DoError(myErr);
  191.  
  192.         Add_Multicast_Address := myErr;
  193.     END;
  194.  
  195.  
  196. {*****************************************************************************}
  197. { The following procedure is a sample completion routine. Note that the routine }
  198. { reuses the parameter block to issue a new ERead call.  It's important to }
  199. { remember that when the completion routine is called, the A5 world will likely}
  200. { be different from that of the application and must be set up correctly before}
  201. { accessing any globals.  In setting up the A5 world, save the original value so}
  202. { that it can be restored upon exit }
  203. {****************************************************************************}
  204.     PROCEDURE MyCompRoutine (myEPBPtr: MyEParamBlkPtr);
  205.  
  206.         VAR
  207.             myErr: OSErr;
  208.             saveA5: LONGINT;
  209.             aptr: Ptr;
  210.  
  211.     BEGIN
  212.         DebugStr('Entering completion routine;g');
  213.         IF (myEPBPtr^.pb.ioResult < noErr) THEN            { was ERead successful }
  214.             BEGIN
  215.                 IF (myEPBPtr^.pb.ioResult <> reqAborted) THEN
  216.                                                     { was request aborted? }
  217.                     DoError(myEPBPtr^.pb.ioResult)            { something bad happened }
  218.             END
  219.         ELSE
  220.             BEGIN                                    { process the packet }
  221.                 saveA5 := SetA5(myEPBPtr^.myA5);        { Set A5 to our world }
  222.                 aptr := myEPBPtr^.pb.EPointer;
  223.                 ProcessData(aptr);                        { do something with the data }
  224.                 saveA5 := SetA5(saveA5);    { restore the A5 World }
  225.             END;
  226.  
  227.  
  228.         IF NOT gDone THEN                        { check whether we have been called because }
  229.                                                     { we're about to quit }
  230.             BEGIN                                    { if not, then make another ERead call }
  231.                 myErr := ERead(EParamBlkPtr(myEPBPtr), TRUE);
  232.                                                     { call ERead to wait for the next packet }
  233.                 IF myErr <> noErr THEN        { check if error occured queueing ERead call }
  234.                     DoError(myErr);
  235.             END;
  236.     END; { of MyCompletion routine }
  237.  
  238.  
  239. {*****************************************************************************}
  240. { The following inline function demonstrates how get the parameter block }
  241. { pointer in register A0.  This is needed by the completion routine to set }
  242. { up the A5 world correctly }
  243. {****************************************************************************}
  244.     FUNCTION GetParamBlockPtr: Ptr;        { Get the pointer to the parameter block out of }
  245.                                                     { A0 and place it on the stack in the location }
  246.                                                     { reserved for it }
  247.     INLINE
  248.         $2E88;            { MOVE.L        A0,(SP) }
  249.  
  250. {*****************************************************************************}
  251. { The following procedure is a stub completion routine which is designed to  }
  252. { get the parameter block pointer in A0 and call the real completion routine. }
  253. { It is possible that prior to calling the inline function, A0 could be used }
  254. { by the compiler for some other purpose.  To minimize the chances of this }
  255. { happening, we do as little as possible in this procedure - grab register A0 }
  256. { then call another procedure where the real work will be performed. }
  257. {****************************************************************************}
  258.     PROCEDURE MyStubCompRoutine;
  259.  
  260.         VAR
  261.             myEPBPtr: MyEParamBlkPtr;
  262.  
  263.     BEGIN
  264.         myEPBPtr := MyEParamBlkPtr(GetParamBlockPtr);    { Get the PB ptr from A0 }
  265.         myCompRoutine(myEPBPtr);                                { call real comp routine }
  266.     END; { of MyCompletion routine }
  267.  
  268.  
  269. {*****************************************************************************}
  270. { The following function demonstrates how to call EAttachPH to use the }
  271. { default handler, then to issue the ERead call to handle any incoming }
  272. { packets.  This routine returns a result of type OSErr to indicate whether }
  273. { an error occured during the process }
  274. {****************************************************************************}
  275.     FUNCTION Sample_AttachPH_And_Read_Packet (ENETRefNum: INTEGER): OSErr;
  276.  
  277.         CONST
  278.             kBigBytes = 8888;
  279.  
  280.         VAR
  281.             myEPBPtr: MyEParamBlkPtr;
  282.             aptr: Ptr;
  283.             myErr: OSErr;
  284.  
  285.     BEGIN        {Sample_AttachPH_And_Read_Packet }
  286.         myEPBPtr := @gMyEPB;                        { set up EAttachPH parameters }
  287.         WITH gMyEPB.pb DO
  288.             BEGIN
  289.                 eProtType := kMyProtocol;        { protocol type }
  290.                 ePointer := NIL;                    { indicate use of default handler }
  291.                 ioRefNum := ENETRefNum;            { ENET driver refNum }
  292.             END;
  293.         myErr := EAttachPH(EParamBlkPtr(myEPBPtr), FALSE); { tell ENET about new }
  294.                                                         { to handle }
  295.  
  296.         IF myErr <> noErr THEN        { check if error occured attaching protocol }
  297.             DoError(myErr)
  298.         ELSE
  299.             BEGIN
  300.                 aptr := NewPtr(kBigBytes);        { need to check the result of this memory }
  301.                 gMyEPB.myA5 := SetCurrentA5;    { store the current a5 world }
  302.                 WITH gMyEPB.pb DO                    { allocation }
  303.                     BEGIN
  304.                         ioCompletion := @MyStubCompRoutine;    { pointer to completion routine }
  305.                         eProtType := kMyProtocol;        { protocol type to respond to }
  306.                         ePointer := aptr;                    { pointer to read-data area }
  307.                         eBuffSize := kBigBytes;            { size of read - data area }
  308.                         ioRefNum := ENETRefNum;            { set ENET driver refNum }
  309.                     END;
  310.                 myErr := ERead(EParamBlkPtr(myEPBPtr), TRUE);
  311.  
  312.                 IF myErr <> noErr THEN        { check if error occured queueing read request }
  313.                     BEGIN
  314.                         DoError(myErr);                    { process error result }
  315.                         Detach_SamplePH(ENETRefNum);    { detach the protocol handler }
  316.                     END;
  317.             END;
  318.         Sample_AttachPH_And_Read_Packet := myErr;    { return the error result }
  319.     END;        {Sample_AttachPH_And_Read_Packet }
  320.  
  321. {*****************************************************************************}
  322. { The following function demonstrates how to send a sample Ethernet packet    }
  323. { For this sample, a multicast address is used instead of a local hardware       }
  324. { address.  The function returns an result of type OSErr                                }
  325. {*****************************************************************************}
  326.     FUNCTION Send_Sample_ENET_Packet (ENETRefNum: Integer): OSErr;
  327.         CONST
  328.             kSIZE1 = 100;
  329.             kSIZE2 = 333;
  330.  
  331.         TYPE
  332.             WDS = RECORD                        { write-data structure }
  333.                     length: INTEGER;            { length of nth entry }
  334.                     aptr: Ptr;                    { pointer to nth entry }
  335.                 END;
  336.  
  337.         VAR
  338.             myWDS: ARRAY[1..4] OF WDS;
  339.             myPB: EParamBlock;                { .ENET parameter block }
  340.             wheader: PACKED ARRAY[0..13] OF Byte;
  341.             stuff1: PACKED ARRAY[1..kSIZE1] OF Byte;
  342.             stuff2: PACKED ARRAY[1..kSIZE2] OF Byte;
  343.             myErr: OSErr;
  344.  
  345.     BEGIN
  346.         { set up the write header }
  347.         { use the multicast address as the destination address and }
  348.         { copy to the first 6 bytes of the wheader array }
  349.         BlockMove(@gMultiCastAddr, @wheader, sizeof(gMultiCastAddr));
  350.  
  351.                                             { bytes 7 -12 are reserved for the }
  352.                                             { source ENET address }
  353.  
  354.         wheader[12] := $90;            { protocol type }
  355.         wheader[13] := $90;            { this must match kProtocol value }
  356.  
  357.         myWDS[1].length := 14;
  358.         myWDS[1].aptr := @wheader;
  359.         myWDS[2].length := kSIZE1;
  360.         myWDS[2].aptr := @stuff1;
  361.         myWDS[3].length := kSIZE2;
  362.         myWDS[3].aptr := @stuff2;
  363.         myWDS[4].length := 0;
  364.  
  365.         myPB.ePointer := @myWDS;
  366.         myPB.ioRefNum := ENETRefNum;
  367.  
  368.         myErr := EWrite(@myPB, FALSE);    { send something }
  369.         IF myErr <> noErr THEN
  370.             DoError(myErr);
  371.         Send_Sample_ENET_Packet := myErr;
  372.     END;
  373.  
  374.  
  375. {*****************************************************************************}
  376. { The following procedure demonstrates how to call EDetachPH to remove the }
  377. { specified protocol from the list of protocols on which the ENET driver should }
  378. { respond to }
  379. {****************************************************************************}
  380.     PROCEDURE Detach_SamplePH (ENETRefNum: INTEGER);
  381.         VAR
  382.             myPB: EParamBlock;
  383.             myErr: OSErr;
  384.     BEGIN
  385.         myPB.eProtType := kMyProtocol;
  386.         myPB.ioRefNum := ENETRefNum;
  387.         myErr := EDetachPH(@myPB, FALSE);
  388.     END;
  389.  
  390.  
  391. {*****************************************************************************}
  392. { The following procedure demonstrates how to call EDelMulti to remove the }
  393. { a multicast address from the list of addresses that the driver should accept }
  394. { packets for }
  395. {****************************************************************************}
  396.     PROCEDURE Delete_Multicast_Address (ENETRefNum: INTEGER);
  397.         VAR
  398.             myPB: EParamBlock;
  399.             myErr: OSErr;
  400.     BEGIN
  401.             { see note above in Add_Multicast_Address function code }
  402.         BlockMove(@gMultiCastAddr, @myPB.eMultiAddr, sizeof(gMultiCastAddr));
  403.         myPB.ioRefNum := ENETRefNum;
  404.         myErr := EDelMulti(@myPB, FALSE);
  405.         IF myErr <> noErr THEN
  406.             DoError(myErr);
  407.     END;
  408.  
  409.  
  410. {*****************************************************************************}
  411. { The following procedure demonstrates how to call ERdCancel to remove a }
  412. { queued read request to the ENET driver }
  413. {****************************************************************************}
  414.     PROCEDURE Call_ERdCancel;
  415.         VAR
  416.             myPB: EParamBlock;
  417.             myErr: OSErr;
  418.     BEGIN
  419.         myPB.ePointer := Ptr(@gMyEPB);
  420.         myPB.ioRefNum := gMyEPB.pb.ioRefNum;
  421.         myErr := ERdCancel(@myPB, FALSE);
  422.     END;
  423.  
  424.  
  425.  
  426. {*****************************************************************************}
  427. { Here's the MAIN program where we put everything together }
  428. {****************************************************************************}
  429.  
  430.     VAR
  431.         myErr: OSErr;
  432.         ENETRefNum: INTEGER;
  433.  
  434. BEGIN
  435.     gDone := FALSE;                { initialize gDone }
  436.     Init_Multicast_Address;
  437.     ENETRefNum := Get_And_Open_ENET_Driver;
  438.     IF ENETRefNum <> 0 THEN
  439.         BEGIN
  440.             myErr := Add_Multicast_Address(ENETRefNum);
  441.             IF myErr = noErr THEN
  442.                 BEGIN
  443.                     myErr := Sample_AttachPH_And_Read_Packet(ENETRefNum);
  444.  
  445.                     IF myErr = noErr THEN
  446.                         BEGIN
  447.  
  448.                             myErr := Send_Sample_ENET_Packet(ENETRefNum);
  449.                             IF myErr <> noErr THEN
  450.                                 DoError(myErr);
  451.  
  452.         { do application defined tasks here }
  453.  
  454.                             REPEAT
  455.                             UNTIL Button;
  456.                         END;
  457.                     gDone := TRUE;            { indicate that we are getting ready to quit }
  458.                     IF gMyEPB.pb.ioResult = 1 THEN
  459.                         Call_ERdCancel;
  460.                     Detach_SamplePH(ENETRefNum);
  461.  
  462.                 END;
  463.             Delete_Multicast_Address(ENETRefNum);
  464.         END;
  465. END.